\section{项目的主要内容和技术路线}
项目的主要内容为 Windows 下的 WebSocket C++ 服务器实现，具体有以下几个要点：
\begin{itemize}[topsep=0pt]
    \setlength{\itemindent}{0pt}
          \setlength{\itemsep}{0pt}
          \setlength{\parsep}{0pt}
          \setlength{\parskip}{0pt}
    \item 研究 Windows 上的异步 I/O 模型 IOCP 以及相关的线程池模型，Windows 平台网络编程 API，并将这些 API 封装为现代 C++ 风格的接口
    \item 实现合理的 C++ 日志库以提供清晰的日志服务；实现 WebSocket 协议，并正确处理 TCP 基于流的数据传输；对服务器的性能进行测试和调优
    \item 通过 nginx 进行反向代理实现 WebSocket Secure , 保证传输数据的安全性
    \item 提供一个有效的客户端框架和具体实例，用来测试服务器的有效性和性能
\end{itemize}

\subsection{技术路线}

\subsubsection{Windows 下的 IOCP 框架}
I/O 完成端口是一种用于 Windows NT 3.5 以及更高版本等操作系统中同时执行多个异步 I/O 操作的 API 。高性能服务器中往往使用的是并发模型，即每个客户端连接都会使用单独的线程进行处理，虽然能防止服务器因为处理一个客户端的请求而阻塞了对其它客户端的服务，但同时也会导致系统中存在大量运行中的线程，在上下文切换中浪费了大量的资源。I/O 完成端口则是对此问题的一个解决方案，其背后的理论是：并发的线程数量总会有一个上限，当超过这个上限时，多余的线程将不会提高处理任务的效率\cite{windowsviacpp}。
\par
我们首先需要通过 CreateIoCompletionPort 来创建出一个可用的 I/O 完成端口，然后将用 WSASocket 创建出的套接字关联到完成端口上。在套接字完成了绑定端口并开始监听后，我们就可以向完成端口投递异步的接收连接操作了。在完成端口上投递的请求中包含一个重叠结构指针，和一个用户定义的 PULONG\_PTR 类型的 Completion Key。重叠结构（重叠代表着执行 I/O 与其线程执行其它任务是并行的，在时间上处于重叠状态）在每一次异步 I/O 操作中是唯一的，用来记录这次的异步 I/O 执行的状态和结果。对于其它每次 I/O 操作相关的独有数据，我们可以通过扩展重叠结构（使用继承或者包含皆可）来记录投递的 I/O 操作类型，以及保存 I/O 操作的缓冲区等。Completion Key 可以用来保存当前的 I/O 操作是建立在哪个套接字上的，以及该套接字上存在的 I/O 操作数据。
\par
在完成端口上投递的所有 I/O 请求可以通过 GetQueuedCompletionStatus 来获取 I/O 完成时通知，在没有完成的 I/O 请求时 GetQueuedCompletionStatus 会阻塞，直到产生相应的完成事件时线程被唤醒。我们可以设置和硬件支持的并发数量的工作线程来重复调用此函数以执行服务器上的 I/O 。工作线程在被唤醒后可以获取到四个数据：在发起 I/O 时传入的 Completion Key 和重叠结构指针、本次 I/O 传输的字节数、本次 I/O 传输的结果。工作线程可以根据这些数据判断上次 I/O 的完成结果，来决定接下来的行为。Windows 在 Vista 之后提供了全新的线程管理 API \cite{threadpoolapi}，可以将由多个线程管理的 I/O 操作通过操作系统提供的线程池 API 进行管理。此线程池能够根据 I/O 的压力大小动态调整工作线程的数量。在使用这套 API 时，需要使用 CreateThreadPoolIO 来创建 I/O 完成端口，并在每一次提交 I/O 请求前使用 StartThreadPoolIO 来保证重叠 I/O 的结果能被 I/O 完成端口所接收。在服务器关闭时，同样也需要使用 CloseThreadPoolIO 保证 I/O 完成端口以及相关的线程池资源被正确释放。

\par

在服务器开发中，合理地使用日志能够让开发者能够更清楚地了解服务器的运行状况，在某些难以调试的场景中日志将会是解决程序问题的最好办法。合理的日志库应该能够提供不同的日志级别，在多线程场景下使用同步手段防止日志错乱，可以使用异步的日志输出来提高性能。

\subsubsection{WebSocket 协议的实现}

WebSocket 协议的实现包括：WebSocket 握手，数据分帧，数据遮蔽。数据帧的定义将会根据 RFC 6445 进行解析，主要区分为数据传输帧和控制帧，或者是测试客户端/服务器是否存活的测试帧。握手阶段客户端需要向服务器发送一份 HTTP 协议的 WebSocket 握手信息，其中包含了一份随机生成的 Sec-WebSocket-Key，服务器在收到后需要使用 base54 对其进行解码再使用 SHA-1 将数据通过特殊的固定模板哈希生成 Sec-WebSocket-Accept。客户端在验证后确认无误则 WebSocket 连接完成。客户端对服务器发送的数据必须经过掩蔽，客户端需要自行生成随机的 32 位掩码，并将发送数据与其进行按位异或。协议规定双方都需要支持 Ping/Pong 消息的收发，这种控制帧可以用来判断连接是否中断在乙方需要主动中断连接时可以发送中止帧使 WebSocket 正常关闭。
\par
由于 TCP 传输是以字节流的形式，所以传输得到的可能会是超过一个或仅有半个的 WebSocket 帧，所以在完成端口获得接收操作完成的信息时，我们需要考虑这些数据可能包含着不完整的 WebSocket 帧，拼接/切分这些数据就需要依赖于 WebSocket 数据帧的格式以及其中的长度字段。在与客户端的 WebSocket 握手完成后，应该首先等待接收到足够拼接为一个帧头长度的数据，并根据里面的长度判断接下来还需要接收多少数据才是一个完整的数据帧。

\subsection{可行性分析}

WebSocket 的服务端目前已经有很多实现，其中提供了 C++ 的库包括：POCO C++ Libraries、 QtWebSocket、Wt、Boost.Beast 、websocket++ 等，而主流的浏览器也基本实现了 WebSocket 的客户端功能。WebSocket 在类似的工业界场景中也早已开始发挥作用，例如  \textit{Communicating and Displaying} \cite{pimentel2012communicating} 中提到的一种基于 WebSocket 的实时图像传输，作者对比了 HTTP 轮询与 WebSocket 的两种服务器实现所产生的时延并得到了积极的结论。我们可以认为，在对于低时延和与服务器频繁交换大量数据的这种场景中，使用 WebSocket 作为信息交换的载体是一个不错的选择。

\par

Windows 上的 IOCP 框架则已经拥有数十年的历史，早已在各种工业级的 Windows Server 中得到了验证，是事实上的最适合服务器 I/O 的一种 I/O 模型。IOCP 模型在性能上比 Linux 上的 epoll 模型要略胜一筹，而在 CPU 占用上，因为 IOCP 使用的 CPU 调度方式更为合理，比 epoll 要少将近一半\cite{iocpvsepoll}。
我们有理由相信，基于 IOCP 来设计 Windows 上的服务器模型是具有很强的可操作性和可行性的。

\par

由于项目的服务器部分最终是通过 nginx 的反向代理功能实现，所以我们只需要在客户端框架中提供可选的 TLS 支持即可。OpenSSL 是一个强大的安全套接字密码库，实现了目前大多数广泛使用的密码学算法，支持 TLS1.3 等最新的安全通信协议。客户端中我们可以借助 OpenSSL 的文档，使用 OpenSSL 提供的相关 API 来支持安全传输层协议，也是很容易实现的。

